package org.daochong.lang;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class SimpleHttpClient {

	class DaochongHostnameVerifier implements HostnameVerifier {

		private boolean validata = false;

		public DaochongHostnameVerifier() {

		}

		public DaochongHostnameVerifier(boolean validata) {
			this.validata = validata;
		}

		public boolean verify(String host, SSLSession session) {
			if (!this.validata)
				return true;
			try {
				String[] ar = session.getPeerPrincipal().getName().split(",");
				for (String str : ar) {
					int pos = str.indexOf("=");
					if (pos <= 0)
						continue;
					String key = str.substring(0, pos).trim();
					String val = str.substring(pos + 1).trim();
					if (key.equals("CN")) {
						return host.equals(val);
					}
				}
				return false;
			} catch (Throwable e) {
				return false;
			}
		}
	}

	public class DaochongSSLFactory extends SSLSocketFactory {

		private SSLContext sslContext;

		public DaochongSSLFactory(SSLContext sslContext) {
			this.sslContext = sslContext;
		}

		public Socket createSocket(InetAddress host, int port) throws IOException {
			return this.sslContext.getSocketFactory().createSocket(host, port);
		}

		public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
				throws IOException {
			return this.sslContext.getSocketFactory().createSocket(address, port, localAddress, localPort);
		}

		public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
			return this.sslContext.getSocketFactory().createSocket(s, host, port, autoClose);
		}

		public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
			return this.sslContext.getSocketFactory().createSocket(host, port);
		}

		public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
				throws IOException, UnknownHostException {
			return this.sslContext.getSocketFactory().createSocket(host, port, localHost, localPort);
		}

		public String[] getDefaultCipherSuites() {
			return this.sslContext.getSocketFactory().getDefaultCipherSuites();
		}

		public String[] getSupportedCipherSuites() {
			return this.sslContext.getSocketFactory().getSupportedCipherSuites();
		}

	}

	class DaochongTrustManager implements X509TrustManager {
		private X509TrustManager sunJSSEX509TrustManager;
		private X509Certificate[] serverCertificates;

		public DaochongTrustManager() {

		}

		public DaochongTrustManager(KeyStore keyStore) throws Exception {
			if (keyStore == null)
				return;
			TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509", "SunJSSE");
			tmf.init(keyStore);
			TrustManager tms[] = tmf.getTrustManagers();
			for (int i = 0; i < tms.length; i++) {
				if (tms[i] instanceof X509TrustManager) {
					sunJSSEX509TrustManager = (X509TrustManager) tms[i];
					return;
				}
			}
			throw new RuntimeException("Couldn't initialize TrustManager");
		}

		public DaochongTrustManager(X509Certificate[] serverCertificates) {
			this.serverCertificates = serverCertificates;
		}

		public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
			if (sunJSSEX509TrustManager != null) {
				try {
					sunJSSEX509TrustManager.checkClientTrusted(chain, authType);
				} catch (CertificateException excep) {
				}
			} else {
				trusted(chain);
			}
		}

		public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
			if (sunJSSEX509TrustManager != null) {
				try {
					sunJSSEX509TrustManager.checkServerTrusted(chain, authType);
				} catch (CertificateException excep) {
				}
			} else {
				trusted(chain);
			}
		}

		public X509Certificate[] getAcceptedIssuers() {
			if (sunJSSEX509TrustManager != null) {
				return sunJSSEX509TrustManager.getAcceptedIssuers();
			} else
				return this.serverCertificates;
		}

		private void trusted(X509Certificate[] chain) throws CertificateException {
			if (this.serverCertificates != null && this.serverCertificates.length > 0) {
				boolean flag = false;
				for (X509Certificate desc : chain) {
					for (X509Certificate src : this.serverCertificates) {
						if (src.getIssuerDN().getName().equals(desc.getIssuerDN().getName())
								&& src.getSerialNumber().equals(desc.getSerialNumber())) {
							flag = true;
							break;
						}
					}
					if (flag)
						break;
				}
				if (!flag)
					throw new CertificateException("not trusted Cert Chain");
			}
		}
	}

	public static String DEFAULT_USER_AGENT = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; WOW64; Trident/6.0; .NET4.0E; .NET4.0C; .NET CLR 1.1.4322)";
	private Map<String, String> cookies = new LinkedHashMap<String, String>();
	private Map<String, String> heads = new LinkedHashMap<String, String>();
	private String root;

	private boolean init = false;

	private String defaultEncoding = null;
	private int connectTimeout = 0;
	private boolean useCaches = false;

	private HostnameVerifier hostnameVerifier = null;
	private SSLContext sslContext = null;
	private String sslContextProtocal = "TLS";
	private SSLSocketFactory sslSocketFactory = null;
	private boolean https = false;
	private boolean authServer = false;
	private boolean authClient = false;
	private boolean validataHost = false;
	private boolean utfEncode = false;
	private X509Certificate[] serverCertificates;
	private KeyStore trustStore;
	private String trustStorePath;
	private String trustStorePassword;
	private String trustStoreType;
	private String trustStoreProvider;

	private KeyStore keyStore;
	private String keyStorePath;
	private String keyStorePassword;
	private String keyStoreType;
	private String keyStoreProvider;
	private String keyStoreAlgorithm = "SunX509";

	public SimpleHttpClient() {
		heads.put("User-Agent", DEFAULT_USER_AGENT);
		heads.put("Content-Language", "zh-cn");
		heads.put("Connection", "keep-alive");
		heads.put("Cache-Control", "no-cache");
		heads.put("Content-Type", "application/x-www-form-urlencoded");
		heads.put("Accept", "*/*");
	}

	public String getTrustStoreProvider() {
		return trustStoreProvider;
	}

	public void setTrustStoreProvider(String trustStoreProvider) {
		this.trustStoreProvider = trustStoreProvider;
	}

	public String getKeyStoreProvider() {
		return keyStoreProvider;
	}

	public void setKeyStoreProvider(String keyStoreProvider) {
		this.keyStoreProvider = keyStoreProvider;
	}

	public void setKeyStore(KeyStore keyStore) {
		this.keyStore = keyStore;
	}

	public String _RequestString(String method, String url, Map<String, String> params, String encode) {
		return _RequestString(method, url, params, encode, null);
	}

	public String _RequestString(String method, String url, Map<String, String> params, String encode,
			Map<String, File> files) {
		init();
		HttpURLConnection conn = getConnection(url);
		try {
			conn.setRequestMethod(method);
			conn.setDoInput(true);

			if (params != null && params.size() > 0) {
				if (files == null || files.size() == 0) {
					StringBuffer sb = new StringBuffer();
					for (String key : params.keySet()) {
						sb.append("&").append(key).append("=");
						String str = params.get(key);
						if (str != null) {
							if (this.isUtfEncode()) {
								sb.append(StringUtils.utfEncode(str));
							} else if (this.getDefaultEncoding() != null) {

								sb.append(URLEncoder.encode(str, this.getDefaultEncoding()));
							} else {
								sb.append(params.get(key));
							}
						}
					}
					String ps = sb.toString().substring(1);
					conn.setDoOutput(true);
					OutputStream os = conn.getOutputStream();
					os.write(ps.getBytes());
					os.flush();
				} else {
					conn.setDoOutput(true);
					String BOUNDARY = "---------7d4a6d158c9";
					conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
					DataOutputStream out = new DataOutputStream(conn.getOutputStream());
					if (params != null && params.size() > 0) {
						StringBuffer strBuf = new StringBuffer();
						for (String key : params.keySet()) {
							String str = params.get(key);
							if (str != null) {
								if (this.isUtfEncode()) {
									str = StringUtils.utfEncode(str);
								} else if (this.getDefaultEncoding() != null) {
									str = URLEncoder.encode(str, this.getDefaultEncoding());
								}
							}
							strBuf.append("\r\n").append("--").append(BOUNDARY).append("\r\n");
							strBuf.append("Content-Disposition: form-data; name=\"" + key + "\"\r\n\r\n");
							strBuf.append(str);
						}
						out.write(strBuf.toString().getBytes());
					}
					for (String key : files.keySet()) {
						File f = files.get(key);
						StringBuffer strBuf = new StringBuffer();
						strBuf.append("\r\n").append("--").append(BOUNDARY).append("\r\n");
						strBuf.append("Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + f.getName()
								+ "\"\r\n");
						strBuf.append("Content-Type:image/jpeg\r\n\r\n");
						out.write(strBuf.toString().getBytes());
						if (f.exists()) {
							DataInputStream in = new DataInputStream(new FileInputStream(f));
							int bytes = 0;
							byte[] bufferOut = new byte[1024];
							while ((bytes = in.read(bufferOut)) != -1) {
								out.write(bufferOut, 0, bytes);
							}
							in.close();
						}
					}
					byte[] end_data = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();// 瀹氫箟鏈�鍚庢暟鎹垎闅旂嚎
					out.write(end_data);
					out.flush();
					out.close();
				}
			}
			setCookies(conn);
			return getString(conn, encode);
		} catch (Throwable e) {
			throw new RuntimeException(e);
		} finally {
			try {
				conn.disconnect();
			} catch (Throwable e) {

			}
		}
	}

	public String _RequestRedirect(String method, String url, Map<String, String> params, String encode) {
		init();
		HttpURLConnection conn = getConnection(url);
		try {
			conn.setRequestMethod(method);
			conn.setDoInput(true);
			if (params != null && params.size() > 0) {
				StringBuffer sb = new StringBuffer();
				for (String key : params.keySet()) {
					sb.append("&").append(key).append("=");
					String str = params.get(key);
					if (str != null) {
						if (this.isUtfEncode()) {
							sb.append(StringUtils.utfEncode(str));
						} else if (this.getDefaultEncoding() != null) {

							sb.append(URLEncoder.encode(str, this.getDefaultEncoding()));
						} else {
							sb.append(params.get(key));
						}
					}
				}
				String ps = sb.toString().substring(1);
				conn.setDoOutput(true);
				OutputStream os = conn.getOutputStream();
				os.write(ps.getBytes());
				os.flush();
			}
			setCookies(conn);
			try {
				int code = conn.getResponseCode();
				if (code / 100 == 3) {
					String location = conn.getHeaderField("Location");
					if (location != null) {
						conn.disconnect();
						return location;
					}
				}
			} catch (Throwable e) {

			}
		} catch (Throwable e) {
			throw new RuntimeException(e);
		} finally {
			try {
				conn.disconnect();
			} catch (Throwable e) {

			}
		}
		return null;
	}

	public void addCookies(String key, String value) {
		if (cookies.containsKey(key)) {
			cookies.put(key, cookies.get(key) + ", " + value);
		} else
			cookies.put(key, value);
	}

	public void setCookies(String key, String value) {
		cookies.put(key, value);
	}

	public void addHead(String key, String value) {
		heads.put(key, value);
	}

	public void clearCookies() {
		cookies.clear();
	}

	public void clearHeads() {
		heads.clear();
	}

	public SimpleHttpClient clone() {
		SimpleHttpClient client = new SimpleHttpClient();
		client.connectTimeout = this.connectTimeout;
		client.cookies = this.cookies;
		client.defaultEncoding = this.defaultEncoding;
		client.heads = this.heads;
		client.hostnameVerifier = this.hostnameVerifier;
		client.https = this.https;
		client.root = this.root;
		client.sslContext = this.sslContext;
		client.useCaches = this.useCaches;
		return client;
	}

	public String deleteString(String url) {
		return deleteString(url, null, null);
	}

	public String deleteString(String url, Map<String, String> params) {
		return deleteString(url, params, null);
	}

	public String deleteString(String url, Map<String, String> params, String encode) {
		if (params != null) {
			String tmp = "";
			for (String key : params.keySet()) {
				try {
					tmp += "&" + key + "=" + (this.getDefaultEncoding() == null ? params.get(key)
							: URLEncoder.encode(params.get(key), this.getDefaultEncoding()));
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			}
			if (url.indexOf("?") == -1) {
				url = url + "?" + tmp.substring(1);
			} else {
				url += tmp;
			}
		}
		HttpURLConnection conn = getConnection(url);
		try {
			conn.setRequestMethod("DELETE");
			conn.setDoInput(true);
			setCookies(conn);
			return getString(conn, encode);
		} catch (Throwable e) {
			throw new RuntimeException(e);
		} finally {
			try {
				conn.disconnect();
			} catch (Throwable e) {

			}
		}
	}

	public String deleteString(String url, String encode) {
		return deleteString(url, null, encode);
	}

	public byte[] getBytes(String url) {
		return getBytes(url, null);
	}

	public byte[] getBytes(String url, Map<String, String> params) {
		init();
		if (params != null) {
			String tmp = "";
			for (String key : params.keySet()) {
				try {
					tmp += "&" + key + "=" + (this.getDefaultEncoding() == null ? params.get(key)
							: URLEncoder.encode(params.get(key), this.getDefaultEncoding()));
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			}
			if (url.indexOf("?") == -1) {
				url = url + "?" + tmp.substring(1);
			} else {
				url += tmp;
			}
		}
		HttpURLConnection conn = getConnection(url);
		try {
			conn.setRequestMethod("GET");
			conn.setDoInput(true);
			setCookies(conn);
			int code = conn.getResponseCode();
			if (code / 100 == 3) {
				String location = conn.getHeaderField("Location");
				if (location != null) {
					conn.disconnect();
					return getBytes(location);
				}
			}
			if (code == 200) {
				byte[] data = StringUtils.toBytes(conn.getInputStream());
				return data;
			}
			return null;
		} catch (Throwable e) {
			throw new RuntimeException(e);
		} finally {
			try {
				conn.disconnect();
			} catch (Throwable e) {

			}
		}
	}

	public HttpURLConnection getConnection(String url) {
		String u = (this.root == null ? url
				: (url.equals("/") ? this.root
						: (url.startsWith(this.root) ? url : (url.startsWith("/") ? root + url : root + "/" + url))));
		HttpURLConnection conn = null;
		if (this.https) {
			URL ul = null;
			HttpsURLConnection c = null;
			try {
				ul = new URL(u);
				c = (HttpsURLConnection) ul.openConnection();
				c.setSSLSocketFactory(this.getSSLSocketFactory());
				c.setHostnameVerifier(this.hostnameVerifier);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			conn = c;
		} else {
			URL ul = null;
			try {
				ul = new URL(u);
				conn = (HttpURLConnection) ul.openConnection();
			} catch (Throwable e) {
				throw new RuntimeException(e);
			}
		}
		if (this.getConnectTimeout() > 0)
			conn.setConnectTimeout(getConnectTimeout());
		conn.setUseCaches(this.isUseCaches());
		String cookies = "";
		for (String key : this.getCookies()) {
			String value = this.getCookies(key);
			cookies += key + "=" + value + "; ";
		}
		conn.setRequestProperty("Cookie", cookies);
		for (String key : this.getHeads()) {
			String value = this.getHead(key);
			conn.setRequestProperty(key, value);
		}
		return conn;
	}

	public int getConnectTimeout() {
		return connectTimeout;
	}

	public Set<String> getCookies() {
		return cookies.keySet();
	}

	public String getCookies(String key) {
		return cookies.get(key);
	}

	public String getDefaultEncoding() {
		return defaultEncoding;
	}

	public String getHead(String key) {
		return heads.get(key);
	}

	public Set<String> getHeads() {
		return heads.keySet();
	}

	public HostnameVerifier getHostnameVerifier() {
		return hostnameVerifier;
	}

	public KeyStore getKeyStore() {
		if (this.keyStore == null) {
			try {
				if (this.getKeyStoreProvider() != null) {
					this.keyStore = KeyStore.getInstance(
							this.getKeyStoreType() != null ? this.getKeyStoreType() : "JKS",
							this.getKeyStoreProvider());
				} else {
					this.keyStore = KeyStore
							.getInstance(this.getKeyStoreType() != null ? this.getKeyStoreType() : "JKS");
				}
				this.keyStore.load(this.loadByFile(this.getKeyStorePath()), this.getKeyStorePassword().toCharArray());
			} catch (Exception e) {

			}
		}
		return keyStore;
	}

	public String getKeyStoreAlgorithm() {
		return keyStoreAlgorithm;
	}

	public String getKeyStorePassword() {
		return keyStorePassword;
	}

	public String getKeyStorePath() {
		return keyStorePath;
	}

	public String getKeyStoreType() {
		return keyStoreType;
	}

	public String getRoot() {
		return root;
	}

	public X509Certificate[] getServerCertificates() {
		return serverCertificates;
	}

	public SSLContext getSslContext() {
		return sslContext;
	}

	public String getSslContextProtocal() {
		return sslContextProtocal;
	}

	private SSLSocketFactory getSSLSocketFactory() {
		try {
			if (this.sslSocketFactory != null) {
				return this.sslSocketFactory;
			}
			if (this.sslContext != null) {
				this.sslSocketFactory = new DaochongSSLFactory(sslContext);
				return this.sslSocketFactory;
			}
			sslContext = SSLContext.getInstance(this.getSslContextProtocal()); //
			KeyManager[] keys = null;
			TrustManager[] tms = null;
			if (this.getKeyStore() != null) {
				KeyManagerFactory f = KeyManagerFactory.getInstance(this.getKeyStoreAlgorithm());
				if (this.keyStorePassword == null)
					throw new RuntimeException("KeyStore Password is null");
				f.init(this.getKeyStore(), this.keyStorePassword.trim().toCharArray());
				keys = f.getKeyManagers();
			}
			if (this.isAuthServer()) {
				if (this.serverCertificates != null) {
					tms = new TrustManager[] { new DaochongTrustManager(this.serverCertificates) };
				} else {
					tms = new TrustManager[] { new DaochongTrustManager(this.getTrustStore()) };
				}
			} else {
				tms = new TrustManager[] { new DaochongTrustManager() };
			}
			if (this.isAuthServer() && this.getTrustStore() == null
					&& (this.serverCertificates == null || this.serverCertificates.length == 0)) {
				throw new RuntimeException("Server Certificates not init ");
			}
			if (this.isAuthClient() && keys == null) {
				throw new RuntimeException("Client Certificates not init ");
			}
			sslContext.init(keys, tms, new SecureRandom());
			if (this.hostnameVerifier == null) {
				hostnameVerifier = new DaochongHostnameVerifier(this.isValidataHost());
			}

			this.sslSocketFactory = new DaochongSSLFactory(sslContext);
			return this.sslSocketFactory;
		} catch (RuntimeException e) {
			throw e;
		} catch (Exception e) {
			throw new RuntimeException("create SSL Factory Fail", e);
		}
	}

	private String getString(HttpURLConnection conn, String encode) {
		try {
			int code = conn.getResponseCode();
			if (code / 100 == 3) {
				String location = conn.getHeaderField("Location");
				if (location != null) {
					conn.disconnect();
					return getString(location, encode);
				}
			}
			if (code == 200) {
				String type = conn.getHeaderField("Content-Type");
				if (encode == null) {
					if (type != null) {
						String[] tmp = type.split(";");
						if (tmp.length > 1) {
							for (int i = 1; i < tmp.length; i++) {
								String t = tmp[i];
								String tt = t.toLowerCase();
								String f = "charset";
								int pos = tt.indexOf(f);
								if (pos != -1) {
									pos = tt.indexOf("=", (pos + f.length()));
									if (pos != -1) {
										int endPos = tt.indexOf(",", pos + 1);
										if (endPos == -1) {
											endPos = tt.indexOf(" ", pos + 1);
										}
										if (endPos == -1) {
											encode = t.substring(pos + 1).trim();
										} else {
											encode = t.substring(pos + 1, endPos).trim();
										}
									}
								}
							}
						}
					}
				}
				InputStream in = conn.getInputStream();
				if (encode != null) {
					return StringUtils.toString(in, encode);
				} else if (this.getDefaultEncoding() != null) {
					return StringUtils.toString(in, this.getDefaultEncoding());
				} else {
					byte[] data = StringUtils.toBytes(in);
					String re = new String(data).trim();
					String t = type.toLowerCase();
					if (re.startsWith("<?xml")) {

						String chart = "UTF-8";
						String s = "\"";
						int pos = re.indexOf("encoding=" + s);
						if (pos == -1) {
							pos = re.indexOf("encoding='");
							s = "'";
						}
						if (pos > 0) {
							chart = re.substring(pos + 10, re.indexOf(s, pos + 11));
						}
						re = new String(data, chart);
					} else if (t.indexOf("html") != -1) {
						String chart = null;
						String f = "<metahttp-equiv=\"Content-Type\"content=\"text/html;charset=";
						String tmp = re.replaceAll(" ", "");
						int pos = tmp.indexOf(f);
						if (pos > 0) {
							int p1 = tmp.indexOf("\"", pos + f.length());
							if (p1 > 0) {
								chart = tmp.substring(pos + f.length(), p1);
								re = new String(data, chart);
							}
						} else {
							f = "<metacharset=\"";
							pos = tmp.indexOf(f);
							if (pos > 0) {
								int p1 = tmp.indexOf("\"", pos + f.length());
								if (p1 > 0) {
									chart = tmp.substring(pos + f.length(), p1);
									re = new String(data, chart);
								}
							}
						}
					}
					return re;
				}
			} else {
				throw new RuntimeException("ResponseCode=" + code);
			}
		} catch (IOException e) {
			throw new RuntimeException("Process String Fail", e);
		}
	}

	public String getString(String url) {
		return getString(url, null, null);
	}

	public String getString(String url, Map<String, String> params) {
		return getString(url, params, null);
	}

	public String getString(String url, Map<String, String> params, String encode) {
		if (params != null) {
			String tmp = "";
			for (String key : params.keySet()) {
				try {
					tmp += "&" + key + "=" + (this.getDefaultEncoding() == null ? params.get(key)
							: URLEncoder.encode(params.get(key), this.getDefaultEncoding()));
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			}
			if (url.indexOf("?") == -1 && tmp != null && tmp.length() > 0) {
				url = url + "?" + tmp.substring(1);
			} else {
				url += tmp;
			}
		}
		HttpURLConnection conn = getConnection(url);
		try {
			conn.setRequestMethod("GET");
			conn.setDoInput(true);
			setCookies(conn);
			return getString(conn, encode);
		} catch (Throwable e) {
			throw new RuntimeException(e);
		} finally {
			try {
				conn.disconnect();
			} catch (Throwable e) {

			}
		}
	}

	public String getString(String url, String encode) {
		return getString(url, null, encode);
	}

	public String getRedirect(String url) {
		return getRedirect(url, null, null);
	}

	public String getRedirect(String url, Map<String, String> params) {
		return getString(url, params, null);
	}

	public String getRedirect(String url, Map<String, String> params, String encode) {
		if (params != null) {
			String tmp = "";
			for (String key : params.keySet()) {
				try {
					tmp += "&" + key + "=" + (this.getDefaultEncoding() == null ? params.get(key)
							: URLEncoder.encode(params.get(key), this.getDefaultEncoding()));
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			}
			if (url.indexOf("?") == -1) {
				url = url + "?" + tmp.substring(1);
			} else {
				url += tmp;
			}
		}
		HttpURLConnection conn = getConnection(url);
		try {
			conn.setRequestMethod("GET");
			conn.setDoInput(true);
			setCookies(conn);
			try {
				int code = conn.getResponseCode();
				if (code / 100 == 3) {
					String location = conn.getHeaderField("Location");
					if (location != null) {
						conn.disconnect();
						return location;
					}
				}
			} catch (Throwable e) {

			}
		} catch (Throwable e) {
			throw new RuntimeException(e);
		} finally {
			try {
				conn.disconnect();
			} catch (Throwable e) {

			}
		}
		return null;
	}

	public String getRedirect(String url, String encode) {
		return getRedirect(url, null, encode);
	}

	public KeyStore getTrustStore() {
		if (this.trustStore == null) {
			if (this.getTrustStorePath() != null && this.getTrustStorePassword() != null) {
				try {
					if (this.getTrustStoreProvider() == null) {
						this.trustStore = KeyStore
								.getInstance(this.getTrustStoreType() == null ? "JKS" : this.getTrustStoreType());
					} else {
						this.trustStore = KeyStore.getInstance(
								this.getTrustStoreType() == null ? "JKS" : this.getTrustStoreType(),
								this.getTrustStoreProvider());
					}
					this.trustStore.load(loadByFile(this.getTrustStorePath()),
							this.getTrustStorePassword().toCharArray());
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return trustStore;
	}

	public String getTrustStorePassword() {
		return trustStorePassword;
	}

	public String getTrustStorePath() {
		return trustStorePath;
	}

	public String getTrustStoreType() {
		return trustStoreType;
	}

	private void init() {
		if (!this.init) {
			HttpURLConnection conn = getConnection(this.root);
			setCookies(conn);
			conn.disconnect();
			this.init = true;
		}
	}

	public boolean isAuthClient() {
		return authClient;
	}

	public boolean isAuthServer() {
		return authServer;
	}

	public boolean isUseCaches() {
		return useCaches;
	}

	public boolean isUtfEncode() {
		return utfEncode;
	}

	public boolean isValidataHost() {
		return validataHost;
	}

	private InputStream loadByFile(String filePath) throws FileNotFoundException {
		File f = new File(filePath);
		if (f.exists()) {
			return new FileInputStream(filePath);
		} else {
			return SimpleHttpClient.class.getResourceAsStream(filePath);
		}
	}

	public String postString(String url, Map<String, String> params) {
		return postString(url, params, (String) null);
	}

	public String postString(String url, Map<String, String> params, String encode) {
		return _RequestString("POST", url, params, encode);
	}

	public String postString(String url, Map<String, String> params, Map<String, File> files, String encode) {
		return _RequestString("POST", url, params, encode, files);
	}

	public String postString(String url, Map<String, String> params, Map<String, File> files) {
		return _RequestString("POST", url, params, null, files);
	}
	
	public String postRedirect(String url, Map<String, String> params) {
		return postRedirect(url, params, null);
	}

	public String postRedirect(String url, Map<String, String> params, String encode) {
		return _RequestRedirect("POST", url, params, encode);
	}

	public String putString(String url) {
		return putString(url, null, null);
	}

	public String putString(String url, Map<String, String> params) {
		return putString(url, params, null);
	}

	public String putString(String url, Map<String, String> params, String encode) {
		return _RequestString("PUT", url, params, encode);
	}

	public String putString(String url, String encode) {
		return putString(url, null, encode);
	}

	public String putRedirect(String url) {
		return putRedirect(url, null, null);
	}

	public String putRedirect(String url, Map<String, String> params) {
		return putRedirect(url, params, null);
	}

	public String putRedirect(String url, Map<String, String> params, String encode) {
		return _RequestRedirect("PUT", url, params, encode);
	}

	public String putRedirect(String url, String encode) {
		return putString(url, null, encode);
	}

	public void removeCookies(String key) {
		cookies.remove(key);
	}

	public void removeHead(String key) {
		heads.remove(key);
	}

	public void setAuthClient(boolean authClient) {
		this.authClient = authClient;
	}

	public void setAuthServer(boolean authServer) {
		this.authServer = authServer;
	}

	public void setConnectTimeout(int connectTimeout) {
		this.connectTimeout = connectTimeout;
	}

	private void setCookies(HttpURLConnection conn) {
		Map<String, List<String>> map = conn.getHeaderFields();
		for (String key : map.keySet()) {
			if (key == null)
				continue;
			if (key.equalsIgnoreCase("set-cookie")) {
				for (String s : map.get(key)) {
					String tmp = s.substring(0, s.indexOf(";"));
					int pos = tmp.indexOf("=");
					String k = tmp.substring(0, pos).trim();
					String v = tmp.substring(pos + 1).trim();
					cookies.put(k, v);
				}
			}
		}
	}

	public void setDefaultEncoding(String defaultEncoding) {
		this.defaultEncoding = defaultEncoding;
	}

	public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
		this.hostnameVerifier = hostnameVerifier;
	}

	public void setKeyStore(File file, String password) {
		try {
			setKeyStore(new FileInputStream(file), password);
		} catch (FileNotFoundException e) {
			throw new RuntimeException("Set KeyStore Fail(" + file + ")");
		}
	}

	public void setKeyStore(InputStream in, String password) {
		setKeyStore("JKS", in, password);
	}

	public void setKeyStore(KeyStore keyStore, String keyStorePassword) {
		this.keyStore = keyStore;
		this.keyStorePassword = keyStorePassword;
	}

	public void setKeyStore(String type, File file, String password) {
		try {
			setKeyStore(type, new FileInputStream(file), password);
		} catch (FileNotFoundException e) {
			throw new RuntimeException("Set KeyStore Fail(" + file + ")", e);
		}
	}

	public void setKeyStore(String type, InputStream in, String password) {
		try {
			this.setAuthClient(true);
			this.keyStore = KeyStore.getInstance(type);
			keyStore.load(in, password.toCharArray());
			this.keyStorePassword = password;
		} catch (Exception e) {
			throw new RuntimeException("Set KeyStore Fail", e);
		}
	}

	public void setKeyStore(String filePath, String password) {
		try {
			setKeyStore(loadByFile(filePath), password);
		} catch (Exception e) {
			throw new RuntimeException("Set KeyStore Fail(" + filePath + ")", e);
		}
	}

	public void setKeyStore(String type, String filePath, String password) {
		try {
			setKeyStore(type, loadByFile(filePath), password);
		} catch (Exception e) {
			throw new RuntimeException("Set KeyStore Fail(" + filePath + ")", e);
		}
	}

	public void setKeyStoreAlgorithm(String keyStoreAlgorithm) {
		this.keyStoreAlgorithm = keyStoreAlgorithm;
	}

	public void setKeyStorePassword(String keyStorePassword) {
		this.keyStorePassword = keyStorePassword;
	}

	public void setKeyStorePath(String keyStorePath) {
		this.keyStorePath = keyStorePath;
	}

	public void setKeyStoreType(String keyStoreType) {
		this.keyStoreType = keyStoreType;
	}

	public void setRoot(String root) {
		if (root == null) {
			throw new RuntimeException("the root is null");
		} else if (root.length() <= 7) {
			throw new RuntimeException("the root is not Incorrect");
		} else {
			if (root.toLowerCase().startsWith("https")) {
				this.https = true;
			}
		}
		if (root.endsWith("/")) {
			this.root = root.substring(0, root.length() - 1);
		} else {
			this.root = root;
		}

	}

	public void setServerCertificates(File path) {
		try {
			this.setAuthServer(true);
			CertificateFactory factory = CertificateFactory.getInstance("X509");
			this.serverCertificates = new X509Certificate[1];
			this.serverCertificates[0] = (X509Certificate) factory.generateCertificate(new FileInputStream(path));
		} catch (Exception e) {
			throw new RuntimeException("load Server Certificates fail", e);
		}
	}

	public void setServerCertificates(InputStream in) {
		try {
			this.setAuthServer(true);
			CertificateFactory factory = CertificateFactory.getInstance("X509");
			this.serverCertificates = new X509Certificate[1];
			this.serverCertificates[0] = (X509Certificate) factory.generateCertificate(in);
		} catch (Exception e) {
			throw new RuntimeException("load Server Certificates fail", e);
		}
	}

	public void setServerCertificates(String path) {
		try {
			this.setAuthServer(true);
			CertificateFactory factory = CertificateFactory.getInstance("X509");
			this.serverCertificates = new X509Certificate[1];
			this.serverCertificates[0] = (X509Certificate) factory.generateCertificate(loadByFile(path));
		} catch (Exception e) {
			throw new RuntimeException("load Server Certificates fail", e);
		}
	}

	public void setServerCertificates(String... paths) {
		try {
			this.setAuthServer(true);
			CertificateFactory factory = CertificateFactory.getInstance("X509");
			this.serverCertificates = new X509Certificate[paths.length];
			int i = 0;
			for (String path : paths) {
				this.serverCertificates[i++] = (X509Certificate) factory.generateCertificate(loadByFile(path));
			}
		} catch (Exception e) {
			throw new RuntimeException("load Server Certificates fail", e);
		}
	}

	public void setServerCertificates(X509Certificate... serverCertificates) {
		if (serverCertificates != null && serverCertificates.length > 0) {
			this.setAuthServer(true);
		}
		this.serverCertificates = serverCertificates;
	}

	public void setSslContext(SSLContext sslContext) {
		this.sslContext = sslContext;
		this.sslSocketFactory = null;
	}

	public void setSslContextProtocal(String sslContextProtocal) {
		this.sslContextProtocal = sslContextProtocal;
	}

	public void setTrustStore(File file, String password) {
		try {
			setTrustStore(new FileInputStream(file), password);
		} catch (FileNotFoundException e) {
			throw new RuntimeException("Set KeyStore Fail(" + file + ")", e);
		}
	}

	public void setTrustStore(InputStream in, String password) {
		setTrustStore("JKS", in, password);
	}

	public void setTrustStore(KeyStore trustStore) {
		this.trustStore = trustStore;
	}

	public void setTrustStore(String type, File file, String password) {
		try {
			setTrustStore(type, new FileInputStream(file), password);
		} catch (FileNotFoundException e) {
			throw new RuntimeException("Set KeyStore Fail(" + file + ")", e);
		}
	}

	public void setTrustStore(String type, InputStream in, String password) {
		try {
			this.trustStore = KeyStore.getInstance(type);
			trustStore.load(in, password.toCharArray());
			this.setAuthServer(true);
		} catch (Exception e) {
			throw new RuntimeException("Set KeyStore Fail", e);
		}
	}

	public void setTrustStore(String filePath, String password) {
		try {
			setTrustStore(new FileInputStream(filePath), password);
		} catch (FileNotFoundException e) {
			throw new RuntimeException("Set KeyStore Fail(" + filePath + ")", e);
		}
	}

	public void setTrustStore(String type, String filePath, String password) {
		try {
			setTrustStore(type, new FileInputStream(filePath), password);
		} catch (FileNotFoundException e) {
			throw new RuntimeException("Set KeyStore Fail(" + filePath + ")", e);
		}
	}

	public void setTrustStorePassword(String trustStorePassword) {
		this.trustStorePassword = trustStorePassword;
	}

	public void setTrustStorePath(String trustStorePath) {
		this.trustStorePath = trustStorePath;
	}

	public void setTrustStoreType(String trustStoreType) {
		this.trustStoreType = trustStoreType;
	}

	public void setUseCaches(boolean useCaches) {
		this.useCaches = useCaches;
	}

	public void setUtfEncode(boolean utfEncode) {
		this.utfEncode = utfEncode;
	}

	public void setValidataHost(boolean validataHost) {
		this.validataHost = validataHost;
	}

}